﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Imaginarium.battleSistem;
using Imaginarium.team;
using Imaginarium.gui;
using GameEngine.animations;
using Imaginarium.misc;
using GuiLibrary;
using GameEngine.Scenes;
namespace Imaginarium.Scenes
{
    public class BattleScene : Scene, IOnFinishedTakingAction, IOnCharacterAffected, IOnAnimationFinishedListener, IGeneralListener
    {
        #region Attributes
        public TurnExpert turnExpert;
        private BattleInfoGui battleInfoGui;
        private List<DetailAnimation> dyingAnimations = new List<DetailAnimation>();
        private List<BattleDamageText> damagesOf1Turn = new List<BattleDamageText>(); //all damages made during 1 turn
        private int milisToWait = 0;
        #endregion

        #region Constructors
        public void setBattleAttributes(Background background, Team playersTeam, Team enemyTeam){
            this.background = background;

            Rectangle backgroundRect = background.detailAnimations[background.currentAnimation].getRect();
            Vector2 backgroundSize = new Vector2(backgroundRect.Width, backgroundRect.Height);
            float marginUp = CodeParameters.battleMarginUp, marginDown = CodeParameters.battleMarginDown;
            
            this.turnExpert = new TurnExpert(this);
            int charCount = playersTeam.characters.Count;
            for (int i = 0; i < charCount; i++)
            {
                playersTeam.characters[i].position = new Vector2(
                    backgroundSize.X / 5 * 4, marginUp + (backgroundSize.Y-marginDown-marginUp) / charCount * (i + 1) );
                actorManager.addActor(playersTeam.characters[i]);
            }
            charCount = enemyTeam.characters.Count;
            for (int i = 0; i < charCount; i++)
            {
                enemyTeam.characters[i].position = new Vector2(
                    backgroundSize.X / 5 * (1+i%2/2f) , marginUp + (backgroundSize.Y - marginDown - marginUp) / charCount * (i + 1));
                actorManager.addActor(enemyTeam.characters[i]);
            }
            this.turnExpert.m_Teams.Add(enemyTeam);
            this.turnExpert.m_Teams.Add(playersTeam);
            BattleManager.getInstance().turnExpert = this.turnExpert;
            BattleManager.getInstance().battleScene = this;
        }
        public BattleScene(Background background, Team playersTeam, Team enemyTeam) :
            base()
        {
            setBattleAttributes(background, playersTeam, enemyTeam);
        }
        public BattleScene(Background background, Team playersTeam) :
            base()
        {
            Team enemyTeam = null; //TODO create the enemy team, probably using BattleManager
            setBattleAttributes(background, playersTeam, enemyTeam);
        }
        public BattleScene(Background background):
            base()
        {
            Team playersTeam = null; //TODO create the players team or an ia controlled team, probably using BattleManager
            Team enemyTeam = null; //TODO create the enemy team, probably using BattleManager
            setBattleAttributes(background, playersTeam, enemyTeam);
        }
        public BattleScene() : //only for testing purpose
            base()
        {
            List<Character> posibleEnemies = CodeParameters.possibleEnemiesHardcoded();
            List<Character> posibleCharacters = CodeParameters.possiblePlayersHardcoded();
            posibleCharacters.AddRange(posibleEnemies);
            Team playersTeam = BattleManager.getInstance().createTeam(posibleCharacters, 4, false);
            Team enemyTeam = BattleManager.getInstance().createTeam(posibleEnemies, 3, true);
            setBattleAttributes(GUIParameters.backgroundForest(), playersTeam, enemyTeam);
        }

        
        #endregion

        #region Functionality

        #region Inherited Behavior 
        public override void LoadContent()
        {
            battleInfoGui = createBattleInfoGui();
            SceneManager.getInstance().scenes.Add(battleInfoGui);
            SubjectCharacterStat.getInstance().addCharAffectedListener(this);
        }
        public override void draw(Microsoft.Xna.Framework.Graphics.SpriteBatch sb)
        {
            sb.Begin();
            background.draw(sb);
            actorManager.draw(sb);
            sb.End();
        }
        public override void handleInput()
        {
            if (InputManager.getInstance().isKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape)) {
                ImaginariumGame.getInstance().Exit();
            }
            if (!turnExpert.isIAControlled())
            { 
                //TODO take users command
            }
            base.handleInput();
        }
        public override void update(Microsoft.Xna.Framework.GameTime gameTime)
        {
            if (milisToWait <= 0)
            {
                BattleManager.getInstance().update(gameTime);
            }
            else
                milisToWait -= gameTime.ElapsedGameTime.Milliseconds;
            actorManager.update(gameTime.ElapsedGameTime.Milliseconds);
            base.update(gameTime);
        }
        #endregion

        public void setNextChar(Character performer)
        {
            if (performer != null)
            {
                SceneManager.getInstance().scenes.Add(createBattleCommandGui(performer));
                battleInfoGui.setSelectedPlayer(performer);
            }
            else 
            {
                battleInfoGui.setSelectedPlayer(null);
            }
        }
        public bool checkEndOfBattle() 
        {
            bool battleLost = BattleManager.getInstance().checkIfNoMorePlayers(); //if it's your last player you lost
            if (battleLost) 
            {

                SubjectAnimation.getInstance().markForRemoveAnimFinishedListener(this);
                SceneManager.getInstance().scenes.Add(createBattleLostGui());
                return battleLost;
            }
            bool battleWon = BattleManager.getInstance().checkIfNoMoreEnemies(); //if it's the last enemy you won
            if (battleWon) 
            {
                SubjectAnimation.getInstance().markForRemoveAnimFinishedListener(this);
                SceneManager.getInstance().scenes.Add(createBattleWonGui());
                return battleWon;
            }
            return battleLost | battleWon;
        }
        public void animateAttack(BattleCommand battleCommand) 
        {
            battleCommand.startAnimation();
            Character charAttacking = BattleManager.getInstance().charAttacking;
            if (!(charAttacking is Player))
            {
                charAttacking.setAnimation(CharacterAnimations.ATTACK);
                DetailAnimation attackDetAnim = charAttacking.getCurrentDetailAnimation();
                /*attackDetAnim.currentFrame = 0;
                charAttacking.color = Color.Red; //ERASE THIS (for testing purpose)
                attackDetAnim.freqFrame = 1000; //ERASE THIS (for testing purpose)
                 */
            }
            SubjectAnimation.getInstance().addAnimFinishedListener(this);
        }
        public List<BattleDamageText> createDamageText(BattleCommand bc) 
        {
            List<BattleDamageText> bdt = damagesOf1Turn;
            BattleDamageText bd = null;
            SpriteFont font = Imaginarium.ImaginariumGame.getInstance().Content.Load<SpriteFont>(CodeParameters.battleCommandFont);
            
            if (bc.executed)
            {
                foreach (DamageMaker dm in bc.damagesDealt) 
                {
                    int totalDge = dm.getTotalDamage();
                    bd = new BattleDamageText(dm.Target, font, (dm.missed? "Miss" : totalDge.ToString()), dm.Target.position,
                        (dm.missed? Color.Red : totalDge >= 0 ? Color.White : Color.Green), this);
                    actorManager.addActor(bd);
                    bdt.Add(bd);
                }
            }
            else
                throw new Exception("not yet executed");

            return bdt;
        }

        #region GuiCreation
        public BattleCommandGui createBattleCommandGui(Character performer) 
        {
            BattleCommandGui bcgui = new BattleCommandGui(performer);
            bcgui.isPopUp = true;
            bcgui.position = new Vector2(350, 550);
            bcgui.LoadContent();
            return bcgui;
        }
        public BattleInfoGui createBattleInfoGui()
        {
            BattleInfoGui bigui = new BattleInfoGui(turnExpert);
            bigui.isPopUp = true;
            bigui.position = new Vector2(0, 550);
            bigui.LoadContent();
            return bigui;
        }
        public Gui createBattleLostGui() 
        {
            GameOverGui gogui = new GameOverGui();
            gogui.isPopUp = true;
            gogui.doesHalt = true;
            gogui.position = new Vector2(375, 290);
            gogui.LoadContent();
            return gogui;
        }
        public BattleResultGui createBattleWonGui()
        {
            BattleResultGui brgui = new BattleResultGui(turnExpert);
            brgui.isPopUp = true;
            brgui.doesHalt = true;
            brgui.position = new Vector2(100, 100);
            brgui.LoadContent();
            return brgui;
        }
        #endregion

        #endregion

        #region Events

        #region IOnFinishedTakingAction Members

        public void onCharacterFinishedTakingAction(Character prevChar, Character nextChar) 
        {
            if (nextChar != null && !turnExpert.isIAControlled()) 
            {
                setNextChar(nextChar);
            }
            else
                setNextChar(null);
        }
        public void onTeamFinishedTakingAction(Team prevTeam, Team nextTeam)
        {
            if (nextTeam != null && !turnExpert.isIAControlled())
            {
                Character nextCharacter = null;
                foreach (Character character in nextTeam.characters) {
                    if (!character.isDead())
                    {
                        nextCharacter = character;
                        break;
                    }
                }
                setNextChar(nextCharacter);
            }
            else
                setNextChar(null);
        }
        public void onCharacterDied(Character dyingCharacter) 
        {
            dyingCharacter.setAnimation(CodeParameters.DyingAnimationName);
            dyingAnimations.Add(dyingCharacter.getCurrentDetailAnimation());
            SubjectAnimation.getInstance().addAnimFinishedListener(this);
        }

        #endregion

        #region IOnAnimationFinishedListener Members

        public List<DetailAnimation> getDetAnimWaitingToFinish()
        {
            List<DetailAnimation> attakersDiers = new List<DetailAnimation>();
            if (milisToWait <= 0)
            {
                attakersDiers.Add(BattleManager.getInstance().charAttacking.getCurrentDetailAnimation());
                attakersDiers.AddRange(dyingAnimations);
            }
            return attakersDiers;
        }

        public void onAnimationFinished(DetailAnimation da)
        {
            BattleManager bm = BattleManager.getInstance();
            Character charAttacking = bm.charAttacking;
            if (dyingAnimations.Contains(da))
            {
                Character character = (Character)(da.actor is Character ? da.actor : null);
                dyingAnimations.Remove(da);
                if (character != null)
                {
                    if(character.dissapearAtDying)
                        actorManager.removeActor(da.actor);
                    checkEndOfBattle();
                }
            }
            if (charAttacking.detailAnimations.Contains(da)) 
            {
                BattleCommand lastCommand;
                if ((lastCommand = bm.executeLastCommand())==null)
                    return;

                createDamageText(lastCommand);
                if(!charAttacking.isDead())
                    if (charAttacking is Player)
                    {
                        charAttacking.setAnimation(charAttacking.getAttackType() == Character.TypeOfAttack.melee ? CodeParameters.StandMeleeAnimationName :
                        charAttacking.getAttackType() == Character.TypeOfAttack.range ? CodeParameters.StandRangeAnimationName :
                        CodeParameters.StandMagicAnimationName);
                    }
                    else 
                    {
                        charAttacking.setAnimation(CharacterAnimations.WALK_DOWN);
                    }
                /*if (!(charAttacking is Player))
                    da.freqFrame = 200; //ERASE THIS (for testing purpose)
                */
                bm.executingCommand = false;
                bm.commandStack.Remove(lastCommand);
            }
            milisToWait += 1000; //wait a second between an attack and the next
        }

        #endregion

        #region IOnCharacterAffected Members

        public void onCharacterAffected(Character charAffected)
        {
            if (charAffected.isDead()) //it died
            {
                onCharacterDied(charAffected);
            }
        }

        #endregion

        #region IGeneralListener Members

        public void onSomethingFinished(object thingFinished)
        {
            if (thingFinished is BattleDamageText)
            {
                actorManager.removeActor((Actor)thingFinished);
            }
        }

        #endregion

        public void onEndTurn()
        {
            SubjectAnimation.getInstance().markForRemoveAnimFinishedListener(this);
            damagesOf1Turn.Clear();
        }

        #endregion
    }
}
